package com.share.tools;

import java.io.File;
import java.io.PrintWriter;

/**
 * @author caifan
 * @desc 雪花算法
 *       来自：https://github.com/beyondfengyu/SnowFlake/blob/master/SnowFlake.java
 *       另一个
 *       https://gitee.com/blueses/snowflake-demo/blob/master/07-snowflake/src/main/java/snowflake07/SnowflakeUtils.java
 *       从2021年7月3日开始算，如果id传到前端，保证id满足js number最大值2^53的前提下，序列号占用7位（127），机房3位
 *       数据中心3位的前提下可以用到2056年5月1日
 * @date 2020/6/12
 */
public class SnowFlakeUtils {
	/**
	 * 起始的时间戳
	 */
	private final static long START_STMP = 1625296210000L;

	/**
	 * 每一部分占用的位数
	 */
	private final static long SEQUENCE_BIT = 7; // 序列号占用的位数 默认12
	private final static long MACHINE_BIT = 2; // 机器标识占用的位数 默认5
	private final static long DATACENTER_BIT = 2;// 数据中心占用的位数 默认5

	/**
	 * 每一部分的最大值
	 */
	private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);
	private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
	private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);

	/**
	 * 每一部分向左的位移
	 */
	private final static long MACHINE_LEFT = SEQUENCE_BIT;
	private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
	private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;

	private long datacenterId; // 数据中心
	private long machineId; // 机器标识
	private long sequence = 0L; // 序列号
	private long lastStmp = -1L;// 上一次时间戳

	public SnowFlakeUtils(long datacenterId, long machineId) {
		if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
			throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
		}
		if (machineId > MAX_MACHINE_NUM || machineId < 0) {
			throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
		}
		this.datacenterId = datacenterId;
		this.machineId = machineId;
	}

	/**
	 * 产生下一个ID
	 *
	 * @return
	 */
	public synchronized long nextId() {
		long currStmp = getNewstmp();
		if (currStmp < lastStmp) {
			throw new RuntimeException("Clock moved backwards.  Refusing to generate id");
		}

		if (currStmp == lastStmp) {
			// 相同毫秒内，序列号自增
			sequence = (sequence + 1) & MAX_SEQUENCE;
			// 同一毫秒的序列数已经达到最大
			if (sequence == 0L) {
				currStmp = getNextMill();
			}
		} else {
			// 不同毫秒内，序列号置为0
			sequence = 0L;
		}

		lastStmp = currStmp;

		return ((currStmp - START_STMP)) << TIMESTMP_LEFT // 时间戳部分
				| datacenterId << DATACENTER_LEFT // 数据中心部分
				| machineId << MACHINE_LEFT // 机器标识部分
				| sequence; // 序列号部分
	}

	private long getNextMill() {
		long mill = getNewstmp();
		while (mill <= lastStmp) {
			mill = getNewstmp();
		}
		return mill;
	}

	private long getNewstmp() {
		return System.currentTimeMillis() + 1576800000000L;
//		 return 3968236800000L;// 2056-05-01 00:00:00毫秒值 9003333959682432
	}

	public static void main(String[] args) throws Exception {
		SnowFlakeUtils snowFlake = new SnowFlakeUtils(2, 3);
		System.out.println(snowFlake.nextId());

		long start = System.currentTimeMillis();
		PrintWriter pWriter = new PrintWriter(new File("D:/test/id1.txt"));
		for (int i = 0; i < (1 << 17); i++) { // System.out.println(snowFlake.nextId());
			pWriter.println(String.valueOf(snowFlake.nextId()));
		}
		pWriter.flush();
		pWriter.close();
		long end = System.currentTimeMillis();
		System.out.println("耗时:" + ((end - start) / 1000) + "s");

	}
}
